/* $Id: lintf.c 7557b5d4d82b 2012/12/02 09:07:16 pgurumur $
 * Copyright (c) 2009 Prabhu Gurumurthy <pgurumur@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/* I Just like only one includes, instead of multiple include statements in
 * .c file.. willing to change once the complexity of the code increases
 */
#include "lintf.h"

static int 
intf_getipv4(lua_State *L)
{
   /* Module passes with errors, then it returns 1, otherwise as pointed out
    * by lua_error function
    */
   int retval = 1;
   /* Get ARGV[1], from lua stack */
   const char *interface = lua_tostring(L, 1);

   /* Open a UDP socket */
   int sock = socket(AF_INET, SOCK_DGRAM, 0);
   if (sock < 0) {
      lua_pushstring(L, strerror(errno));
      retval = lua_error(L);
   }
   else {
      /* Init interface structure and pass in the interface name to get
       * the IPv4 address
       */
      struct ifreq ifr;
      memset(&ifr, '\0', sizeof(struct ifreq));

      strlcpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
      if (ioctl(sock, SIOCGIFADDR, &ifr) < 0) {
         lua_pushstring(L, strerror(errno));
         retval = lua_error(L);
      }
      else {
         /* Once ioctl call passes, get the interface IPv4 address and 
          * pass it to lua stack
          */
         struct sockaddr_in paddr;
         memcpy(&paddr, &ifr.ifr_addr, sizeof(ifr.ifr_addr));
         lua_pushfstring(L, "%s", inet_ntoa(paddr.sin_addr));
      }

      /* Be a good function and close the socket fd, once we are done */
      close(sock);
   }

   return retval;
}

static int 
intf_getipv6(lua_State *L)
{
   int retval = 1;
   /* Interface data struct pointer to have the pointer for all available
    * interface data structures in the system
    */
   struct ifaddrs *ifptr;

   /* Get ARGV[1] from the function */
   const char *interface = lua_tostring(L, 1);

   if (getifaddrs(&ifptr) < 0 ) {
      lua_pushstring(L, strerror(errno));
      retval = lua_error(L);
   }
   else {
      struct ifaddrs *ptr = NULL;
      char buf[4096];
      memset(buf, '\0', 4096);

      /* If I am IPv6, then get the IPv6 address of the interface passed */
      for (ptr = ifptr; ptr; ptr = ptr->ifa_next) { 
         if (ptr->ifa_addr->sa_family == AF_INET6) { 
            if (strcmp(ptr->ifa_name, interface) == 0) { 
               struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ptr->ifa_addr;
               lua_pushfstring(L, "%s", inet_ntop(sin6->sin6_family,
                     (void *)&(sin6->sin6_addr), buf, 4096));
             }
         }
      }

      /* Free data structs, we are done with it */
      freeifaddrs(ifptr);
   }

   return retval;
}
               
static int 
intf_getphyaddr(lua_State *L)
{
   /* Module always, returns one, if module fails retval points to
    * lua_error 
    */
   int retval = 1;

   /* My ifaddrs struct to be used to getting interface structures of the
    * system
    */
   struct ifaddrs *ifptr;

   if (getifaddrs(&ifptr) < 0) {
      lua_pushstring(L, strerror(errno));
      retval = lua_error(L);
   }
   else {
      /* Create a new table for returning the mac address available on the
       * interfaces in the system
       */
      lua_newtable(L);
      struct ifaddrs *ptr = NULL;
      for (ptr = ifptr; ptr; ptr = ptr->ifa_next) {
#ifndef __linux__
         /* Non-Linux defines, because *BSD use AF_LINK for DL data structs */ 
         if (ptr->ifa_addr->sa_family == AF_LINK) {
            struct sockaddr_dl *dlt = (struct sockaddr_dl *)ptr->ifa_addr;
            lua_pushstring(L, ptr->ifa_name);
            lua_pushfstring(L, "%s", ether_ntoa(
                  (struct ether_addr *)LLADDR(dlt)));
            lua_settable(L, -3);
         }
#else
         /* Linux uses PF_PACKET for DL structs */
         if (ptr->ifa_addr->sa_family == PF_PACKET) {
            int mac_length = 6;
            struct sockaddr_ll *dlt = (struct sockaddr_ll *)ptr->ifa_addr;
            if (eth->sll_halen == mac_length) {
               struct ether_addr eth;
               memset(&eth, 0, sizeof(struct ether_addr));
               memcpy(eth.ether_addr_octet, dlt->sll_addr, mac_length);
               lua_pushstring(L, ptr->ifa_name);
               lua_pushfstring(L, "%s", ether_ntoa(&eth));
               lua_settable(L, -3);
             }
         }
#endif
      }

      /* Free the recv interface data structs as we are done with it */
      freeifaddrs(ifptr);
   }

   return retval;
}

LUA_LIB int
luaopen_lintf(lua_State *L)
{
   /* Module init functions, init the type, name and version and pass the 
    * functions that this module can handle
    */
   luaL_newmetatable(L, LMOD_TYPE);
   lua_pushvalue(L, -1);
   luaL_openlib(L, NULL, intf_lib, 0);
   lua_pushliteral(L, "version");
   lua_pushliteral(L, LMOD_VERSION);
   lua_settable(L, -3);
   lua_pushliteral(L, "__index");
   lua_pushvalue(L, -2);
   lua_settable(L, -3);
   lua_setglobal(L, LMOD_NAME);
   return 1;
}
